#include "parser.h"


/**
 * Retrieves the var name from the cl if it exists and fills the appropriate field structure
 * @param line the line to parse
 * @param command a pointer to a command structure to fill
 */
static void getVarNameFromCommandLine(char* line, Command* command);

/**
 * Retrieves the caller object name from command line. Can be found either when a new is found or just before a dot
 * @param line the line to parse
 * @param command a pointer to a command structure to fill
 */
static void getObjectNameFromCommandLine(char* line, Command* command);

/**
 * Retrieves the target object name from command line. Can be found when a waitfor is used
 * @param line the line to parse
 * @param command a pointer to a command structure to fill
 */
static void getTargetObjectNameFromCommandLine(char* line, Command* command);

/**
 * Retrieves the waitall command if it exists
 * @param line the line to parse
 * @param command a pointer to a command structure to fill
 */
static void getWaitAllFromCommandLine(char* line, Command* command);

/**
 * Retrieves the function call parameters from the command line
 * @param line the line to parse
 * @param command a pointer to a command structure to fill
 */
static void getFncParamsFromCommandLine(char* line, Command* command);

/**
 * Init command structure
 * @param command the structure to initialize
 */
static void initCommandStructure(Command* command);

/**
 * Used for debugging purpose. Prints the command structure to check its content
 * @param command the structure to print
 */
static void printCommand(Command command);

/**
 * Raises a parse exception and prints it the stdout
 */
static void raiseParseException(char* exception);

static void raiseParseException(char* exception){
	printf("\nParse exception : %s\n", exception);	
	extern int exceptionRaised;	
	exceptionRaised = 1;
}

static void printCommand(Command command){
	printf("Object : %s\nTarget obj : %s\nVarname : %s\n",
	command.callerObject,
	command.targetObject,
	command.varName
	);
	int i = 0;
	printf("Commands : ");
	while (command.keyword[i++][0] !='\0'){
			printf("%d = %s ;", i, command.keyword[i-1]);
	}
	i = 0;
	printf("Arguments :");
	while (command.fncArgs[i++][0] !='\0'){
			printf("%d = %s ;", i, command.fncArgs[i-1]);
	}
	printf("\n");
}


void parseCommandLine(char* line, Command* command){
	initCommandStructure(command);
	trim(line, line);
	getVarNameFromCommandLine(line, command);
	getObjectNameFromCommandLine(line, command);
	getTargetObjectNameFromCommandLine(line,command);
	getWaitAllFromCommandLine(line, command);
	getFncParamsFromCommandLine(line, command);
	extern int commandFound;
	if (strstr(line, "quit") != NULL){
		strcpy(command -> keyword[commandFound++], "quit");	
	}
	/* Print parsed command */
	/* printCommand(*command); */
}

static void getVarNameFromCommandLine(char* line, Command* command){
	char* equal = strchr(line, '=');
	char copy[MAX_CMDLINE_LEN];
	extern int commandFound;
	strcpy(copy, line);
	if (equal == NULL){
			char* freeCommand = strstr(copy, "free");
			if (freeCommand == NULL){
				return;	
			}
			char* token =  strtok(copy, "free");
			trim(token, token);
			
			char* waitfor = strtok(token, "waitfor");
			if (waitfor != NULL){
				trim(token, token);
				if (strlen(token) == 0){
					raiseParseException("Expecting to find varname after free");
					return;
				}
				strcpy(command->varName, token);
			}
			else {
				strcpy(command->varName, token);
			}
			strcpy(command->keyword[commandFound++], "free");
	}
	else {
		char* token = strtok(copy, "=");
		trim(token, token);			
		if (equal == line){
			raiseParseException("Invalid left part assignment : expecting varname");
			return;
		}			
		strcpy(command->varName, token);
	}
}

static void getObjectNameFromCommandLine(char* line, Command* command){
	char copy[MAX_CMDLINE_LEN];
	strcpy(copy, line);
	char* new = strstr(line, "new");
	extern int commandFound;
	if (new == NULL){
		char* dot = strchr(copy, '.');
		if (dot == NULL) {
			return;
		}
		char* eq = strchr(copy, '=');
		if (eq == NULL){
			strcpy(command->callerObject, strtok(copy, "."));
		}
		else {
			line += (eq - line) + 1;
			trim(line, line);
			if (strlen(line) == 0){
				raiseParseException("Invalid right assignment part");
				return;
			}
			strcpy(command->callerObject, strtok(line, "."));
		}
	}
	else {
		strcpy(command->keyword[commandFound++], "new");
		char* new =  strtok(line, "new");
		char* lpar = strchr(copy, '(');
		char* rpar = strchr(copy, ')');
		if (lpar == NULL || rpar == NULL || (rpar - 1) == lpar) {
			raiseParseException("Expecting object name inside parenthesis new('Objname')");
			return;
		}
		char* objname = strtok(new, lpar);
		char* libname = strtok(lpar+1,rpar);
		trim(objname, objname);
		strcpy(command -> callerObject, objname);
		strreplace(libname, '"', ' ');
		trim(libname, libname);
		strcpy(command -> varName, libname);
	}
}



static void getTargetObjectNameFromCommandLine(char* line, Command* command){
	char* waitfor = strstr(line, "waitfor");
	int len = strlen("waitfor");
	if (waitfor == NULL) return;
	extern int commandFound;
	line = waitfor + len;
	trim(line,line);
	if (strlen(line) == 0){
		raiseParseException("waitfor command expects target object : waitfor objectname");
		return;
	}	
	strcpy(command ->targetObject, line);
	strcpy(command->keyword[commandFound++], "waitfor");
	
}

static void getWaitAllFromCommandLine(char* line, Command* command){
	char* waitall = strstr(line, "waitall");
	if (waitall == NULL) return;
	extern int commandFound;
	strcpy(command->keyword[commandFound++], "waitall");
}

static void getFncParamsFromCommandLine(char* line, Command* command){
		char* fnc = strstr(line, "work");
		if (fnc == NULL) return;
		extern int commandFound;
		strcpy(command -> keyword[commandFound++], "work");
		line = fnc + strlen("work") + 1;
		char* end = strchr(line, ')');
		if (end == NULL){
			raiseParseException("Expecting to find parenthesis after work command");
			return;
		}
		else if (command -> callerObject[0] == '\0'){
			raiseParseException("work() function must be called on object");
			return;
		}
		line[end -line] = '\0';
		char* token = NULL;
		int i = 0;
		while ((token = strtok(line, ",")) != NULL){
			trim(token, token);
			strcpy(command -> fncArgs[i++], token);
			line = NULL;
		}
}

static void initCommandStructure(Command* command){
	command -> callerObject[0] = '\0';
	command -> targetObject[0] = '\0';
	command -> varName[0] = '\0';
	memset(command -> fncArgs, '\0', sizeof(command -> fncArgs));
	memset(command -> keyword, '\0', sizeof(command -> keyword));
}
